import hmac

import hashlib

import base64

import time
import os
import random

import string
import tkinter as tk
from threading import Thread
import wx
import wx.grid
import logging
import json
import asyncio
import aiohttp
import psutil
import websockets
from urllib.parse import quote
import wxasync
from datetime import datetime
import binascii
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import unpad
import sys
from binParse import HexDataParser
from tkinter import messagebox

# print(sys.path)
import logging

logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')

class MyFrame(wx.Frame):
    def __init__(self):
        self.message_lock = asyncio.Lock()
        self.serial_counter = 1  # 初始化序号计数器
        super().__init__(None, title="自动化复检软件", size=(850, 650))
        self.Center()  # 窗口在屏幕上居中
        self.SetIcon(wx.Icon('resource/t.ico'))  # 设置图标
        # self.Bind(wx.EVT_SIZE, self.on_size)  # 监听窗口尺寸更改事件
        self.panel = wx.Panel(self)
        self.service_started = False  # 初始状态为未开启服务
        self.need_to_alert  = False
        self.websocket = None  # 初始化websocket
        self.checkTime  = ''
        self.erpResponse = {}
        self.setup_ui()
        # self.text_ctrl.SetValue("CG2311270860-CDNC")
    def setup_ui(self):
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        text_label = wx.StaticText(self.panel, label="订单号:")


        text_label.SetForegroundColour(wx.Colour(64, 64, 64))  
        hbox.Add(text_label, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT, 15)
        self.text_ctrl = wx.TextCtrl(self.panel, style=wx.TE_PROCESS_ENTER)
        self.text_ctrl.SetBackgroundColour(wx.Colour(255, 255, 255))  # 浅灰色
        self.text_ctrl.SetForegroundColour(wx.Colour(64, 64, 64))  # 深灰色
        # 绑定文本框内容变化事件
        self.text_ctrl.Bind(wx.EVT_TEXT, self.on_text_changed)
        hbox.Add(self.text_ctrl, 2, wx.EXPAND | wx.LEFT | wx.RIGHT, 10)
        self.vbox = wx.BoxSizer(wx.VERTICAL)
        self.vbox.Add(hbox, 0, wx.EXPAND | wx.ALL, 10)
        self.panel.SetSizer(self.vbox)


        self.grid = wx.grid.Grid(self.panel)
        self.grid.CreateGrid(0, 5)
        self.grid.SetBackgroundColour(wx.Colour(255, 255, 255))  # 白色
        self.grid.SetForegroundColour(wx.Colour(64, 64, 64))  # 深灰色
        self.grid.SetDefaultCellBackgroundColour(wx.Colour(240, 240, 240))  # 浅灰色
        self.grid.SetDefaultCellTextColour(wx.Colour(64, 64, 64))  # 深灰色
        self.grid.SetColLabelValue(0, "序号")
        self.grid.SetColLabelValue(1, "sn")
        self.grid.SetColLabelValue(2, "复检时间")
        self.grid.SetColLabelValue(3, "备注信息")
        self.grid.SetColLabelValue(4, "复检结果")
        self.grid.SetDefaultCellAlignment(wx.ALIGN_CENTER, wx.ALIGN_CENTER)
        self.grid.SetRowLabelSize(0)
        self.grid.EnableEditing(False)
        self.grid.EnableDragGridSize(False)
        self.grid.SetMargins(0, 0)
        self.grid.SetSelectionMode(wx.grid.Grid.SelectRows)
        self.grid.EnableDragRowSize(False)
            # 设置格线颜色来模拟边框效果
        self.grid.SetGridLineColour(wx.BLACK)
        font = wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)
        self.grid.SetDefaultCellFont(font)
        self.vbox.Add(self.grid, 1, wx.EXPAND | wx.ALL, 20)


        # 绑定窗体大小变化事件
        self.Bind(wx.EVT_SIZE, self.on_resize)

        hbox_btn = wx.BoxSizer(wx.HORIZONTAL)
        self.add_button = wx.Button(self.panel, label="关闭box服务")
        self.add_button.Bind(wx.EVT_BUTTON, self.on_add_row)
        hbox_btn.Add(self.add_button, 0, wx.ALIGN_CENTER | wx.ALL, 10)

        self.reset_button = wx.Button(self.panel, label="重置")
        self.reset_button.Bind(wx.EVT_BUTTON, self.on_reset)
        hbox_btn.Add(self.reset_button, 0, wx.ALIGN_CENTER | wx.ALL, 10)

        self.vbox.Add(hbox_btn, 0, wx.ALIGN_CENTER | wx.ALL, 10)

        self.output_text_ctrl = wx.TextCtrl(
            self.panel, style=wx.TE_READONLY | wx.TE_RICH2
        )
        self.output_text_ctrl.SetBackgroundColour(wx.Colour(255, 255, 255))  # 白色背景
        self.output_text_ctrl.SetForegroundColour(wx.Colour(0, 0, 0))  # 黑色文字
        self.vbox.Add(self.output_text_ctrl, 0, wx.EXPAND | wx.Top | wx.RIGHT, 10)  # 将 proportion 设置为 0

        self.panel.SetSizer(self.vbox)  # 应用sizer到面板

    def on_text_changed(self, event):
      
        # 文本框内容发生改变时调用
        self.checkTime = ''  # 将checkTime重置为空字符串
        if (self.grid and self.grid.GetNumberRows() > 0) or len(self.text_ctrl.GetValue()) == 0:
            self.serial_counter = 1  # 初始化序号计数器
            if self.grid.GetNumberRows() > 0:
              self.grid.DeleteRows(0, self.grid.GetNumberRows())
            self.need_to_alert = False

        event.Skip() 
    def on_resize(self, event):
          # 确保事件传递
          event.Skip()
          
          # 更新列宽
          width = self.GetSize().width - 20
          col_count = self.grid.GetNumberCols()
          if col_count > 0:
              col_width = width // col_count
              for col in range(col_count):
                  self.grid.SetColSize(col, col_width)
          self.grid.ForceRefresh()
    def on_size(self, event):
        self.Layout()  # 在窗口尺寸更改时重新布局
        event.Skip()  # 确保还可以处理其他尺寸相关的默认事件处理器

    def custom_print(self, message):
        # 清空文本框内容并设置新文本
        self.output_text_ctrl.Clear()
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")  # 获取当前时间
        formatted_message = f"{current_time}: {message}"  # 格式化消息
        self.output_text_ctrl.AppendText(formatted_message + '\n')  # 将带时间戳的消息添加到输出文本框中

    def get_current_datetime(self):
        """获取当前日期和时间的字符串表示"""
        return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    def log_message(self, msg, file_path):
        """记录消息到文件"""
        with open(file_path, "a", encoding="utf-8") as log_file:
            log_file.write(msg)

    def setup_logging(self):
        """设置日志文件目录和文件名"""
        today_date = datetime.now()
        folder_path = os.path.join("log", str(today_date.year), f"{today_date.month}月", f"{today_date.day}")

        if not os.path.exists(folder_path):
            os.makedirs(folder_path)

        return os.path.join(folder_path, f"日志信息.txt")


    async def start_websocket(self):
        if self.websocket is not None:
            self.custom_print("A websocket connection already exists.")  # This can help us in debugging
            return

        try:
            # 替换为实际路径
            executable_path = 'C:\\Program Files (x86)\\FS.COMInc\\FS_Service\\FS_Panel.exe'
            process_name = 'FS_Panel.exe'
            is_process_running = any(p.name() == process_name for p in psutil.process_iter())
            if not is_process_running:
                # 使用asyncio执行可执行文件
                await asyncio.create_subprocess_exec(executable_path, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)

            token = await self.get_token()
            if token:
                self.websocket = await self.connect_websocket(token)
            self.service_started = True
        except Exception as e:
            logging.error(f"Main error: {e}")
            self.service_started = False
            self.add_button.SetLabel("点击开启box服务")

    async def connect_websocket(self, token):
        url = f'ws://localhost:56789/signalr/connect?transport=webSockets&clientProtocol=2.1&connectionToken={quote(token)}&connectionData=%5B%7B%22name%22%3A%22myhub%22%7D%5D&tid=9'
        self.websocket = await websockets.connect(url)
        print("WebSocket connected")
        self.custom_print('服务开启成功')
        self.service_started = True
        # async for message in self.websocket:
        #     await self.handle_message(self, message)
        async for message in self.websocket:
            async with self.message_lock:
                await self.handle_message(self, message)
        return self.websocket

    async def stop_websocket(self):
        if self.websocket:
            await self.websocket.close()
        self.websocket = None  # Important: Also set self.websocket to None when the service is stopped
        print("WebSocket closed")

    def on_add_row(self, event):
        if not self.service_started or not self.websocket:
            print('开启服务')
            self.service_started = True
            asyncio.create_task(self.start_websocket())
        else:
            print('关闭服务')
            self.service_started = False
            asyncio.create_task(self.stop_websocket())
            self.custom_print('服务关闭成功')
        self.add_button.SetLabel("关闭box服务" if self.service_started else "开启box服务")

    def on_reset(self, event):
        self.text_ctrl.SetValue("")
        self.checkTime = ''  # 将checkTime重置为空字符串
        self.need_to_alert = False
        self.erpResponse = {}
        if self.grid.GetNumberRows() > 0:
            self.serial_counter = 1  # 初始化序号计数器
            self.grid.DeleteRows(0, self.grid.GetNumberRows())

    async def get_token(self):
        try:
            url = 'http://localhost:56789/signalr/negotiate'

            async with aiohttp.ClientSession() as session:
                async with session.get(url) as response:
                    if response.status == 200:
                        result = await response.json()
                        token = result.get('ConnectionToken')

                        if token:
                            return token

                    else:
                        print(f"Error: {response.status}, {response.reason}")
        except Exception as e:
            self.custom_print('box驱动未打开')
            logging.error(f"Error while getting token: {e}")
            return None




    
    def get_erp_token(self):
      def sign(app_id, secret):

          if not app_id or not secret:

            raise ValueError('sign app_id/secret error')

          def get_random_string(length):

            chars = string.ascii_letters + string.digits

            return ''.join(random.choice(chars) for _ in range(length))

          def get_current_seconds():

            return int(time.time())

          ran_str = get_random_string(10)

          stamp = get_current_seconds()

          query = f'k={app_id}&r={ran_str}&t={stamp}&f=fs'

          hmac_new = hmac.new(secret.encode(), query.encode(), hashlib.sha1)

          hex_result = hmac_new.hexdigest()

          sign_str = hex_result + query

          return base64.b64encode(sign_str.encode()).decode()
      app_id = '8fcd373627fe443446bd52ac80d5e128'

      secret = 'iGJPNdUoOyPVcGlwQPhi'

      sign_value = sign(app_id, secret)

      print(sign_value)

      return sign_value
  

    @staticmethod
    async def handle_message(self, message):  
        file_path = self.setup_logging()
        try:
            data = json.loads(message)
            if 'M' in data and len(data['M']) > 0 and data['M'][0]['A'][1] != 'False':
                # 获取文本框内容
                order_number = self.text_ctrl.GetValue().strip()    

                if not order_number:
                  # 调用函数显示弹窗，此例中设置为3秒后自动关闭
                  # self.on_text_changed(self)
                  return
                # # 检查订单号是否为空
                # if not order_number:
                   
                #     if not self.need_to_alert:
                #       wx.MessageBox("请输入订单号", "警告", wx.OK | wx.ICON_WARNING)
                #       return
                #     self.need_to_alert = True
                #     # # 如果为空，则弹出提醒框并返回，不继续执行
                #     return
                res = json.loads(data['M'][0]['A'][1])['Result']
                if res['SerialNumber'] and res['OriginBase64']:
                    current_time = self.get_current_datetime()
                    msg = f" {current_time}----- 序列号： {res['SerialNumber']}\r\n"
                    self.log_message(msg, file_path)
                    decrypted_str = parse_bin(res['SerialNumber'], res['OriginBase64'])
                    parser = HexDataParser()
                    parsed_data = parser.parse_binary_data(decrypted_str)
                    # 当清点日期为空字符串时，需要重新调用接口获取
                    if len(self.checkTime) == 0:
                      # https://airplan.fs.com/YX_0evWtMz4373v/fs_app.php?interface=mes&type=get_write_info

                      api_url = 'https://airplan.fs.com/YX_0evWtMz4373v/fs_app.php?interface=mes&type=get_write_info'

                      # api_url = 'http://10.36.230.108:5002/pyapi/erptest'
                      headers = {
                          'Authorization': f'{self.get_erp_token()}'  # 将YOUR_TOKEN_HERE替换为实际的token值
                      }
                      # 准备要发送的数据，这里替换为实际参数
                      payload = {
                          'orderNo': self.text_ctrl.GetValue().strip(),
                          'serialNo': str(res['SerialNumber']).split('-')[0]
                      }
                      info = wx.BusyInfo("数据请求中...")
                      wx.Yield()  # 确保显示 busy 对话框
                         # 异步发送 POST 请求并传递数据
                      async with aiohttp.ClientSession() as session:
                          response = await session.post(api_url, headers=headers, data=payload)  # 这里使用 json=payload 表示将 payload 序列化为 JSON 格式
        
                          if response.status == 200:            
                            self.erpResponse =  json.loads(await response.text())  # 获取返回的内容，可能是HTML
                            if self.erpResponse['code'] == 200:
                                self.checkTime = self.erpResponse['data']['checkTime']
                                current_time = self.get_current_datetime()
                                msg = f" {current_time}----- 请求ERP接口获取清点时间： {self.checkTime}\r\n"
                                self.log_message(msg, file_path)
                        
                     
                            if len(self.checkTime) == 0:
                               del info
                               messagebox.showinfo("提示","ERP系统信息为空")
                               return
                          else:
                            current_time = self.get_current_datetime()
                            msg = f" {current_time}--调用erp接口--异常----结果：{response}\r\n"
                            self.log_message(msg, file_path)
                      del info
                              
                        # 订单号 orderNumber 复检路径 nas_path  清点时间 date 序列号 sn
                    
                    
                    serialNumber = parsed_data['serial_number']
                    orderNumber = str(self.text_ctrl.GetValue()).strip()
                    
                    JG_CW_CS_DE_PATH = '\\\\10.36.2.253\\写码文件2\\外购组文件\\蔡甸仓编码文件'
                    PCW_PATH = '\\\\10.36.2.253\\写码文件2\\外购组文件\\前置编码存放位置'
                    # CG_BH_PATH = '\\\\10.36.230.108\\复检测试路径'
                    CG_BH_PATH = '\\\\10.36.2.253\\写码文件2\\写码组\\写码文件'
                    # 默认是 CG_BH_PATH
                    resourcesPath = CG_BH_PATH
                    resourceDate= self.format_datetime(self.checkTime,1)
                    if orderNumber.startswith('JG') or orderNumber.startswith('CW')  or orderNumber.startswith('CS') or orderNumber.startswith('DE'):      
                      resourcesPath = JG_CW_CS_DE_PATH
                      resourceDate= self.format_datetime(self.checkTime,2)
                    elif orderNumber.startswith('PCW'):
                      resourcesPath = PCW_PATH
                      resourceDate = self.format_datetime(self.checkTime,2)
                    
     
                    params = {
                        'sn': serialNumber+'.bin',
                        'orderNumber': orderNumber,
                        'nas_path': resourcesPath,
                        'date': resourceDate,
                    }
                    recheckRes = False
                    # 调用接口查找共享bin文件
                    async with aiohttp.ClientSession() as session:
                      shareApi = 'http://10.36.230.108:5002/pyapi/getSharePublicDataBin'
                      # 设置 Content-Type 为 application/json
                      headers = {'Content-Type': 'application/json'}  
                      # 使用 json 参数而不是 data 参数，aiohttp 会自动处理 JSON 的序列化和设置正确的 Content-Type 头
                      shareResponse = await session.post(shareApi, headers=headers, json=params)
                      if shareResponse.status == 200:        
          
                        shareResponseContent =  json.loads(await shareResponse.text())  # 获取返回的内容，可能是HTML

                        current_time = self.get_current_datetime()
                        msg = f" {current_time}----- 共享中查找bin文件路径： {shareResponseContent['binPath']}\r\n"
                        self.log_message(msg, file_path)
                        if shareResponseContent['binPath'] and len(shareResponseContent['standard_string']) > 0:
                            
                          # 调用接口进行复检对比
                              async with aiohttp.ClientSession() as session:
  
                                shareApi = 'http://10.36.230.108:5002/pyapi/compare_hex_strings_api'
                                # 设置 Content-Type 为 application/json
                                headers = {'Content-Type': 'application/json'}  
                                # 使用 json 参数而不是 data 参数，aiohttp 会自动处理 JSON 的序列化和设置正确的 Content-Type 头
                                compareResponse = await session.post(shareApi, headers=headers, json={
                                    'standard_string_original': str(shareResponseContent['standard_string']).upper(),
                                    'compare_string_original' : decrypted_str,
                                    'specialCodePathDirName': str(shareResponseContent['binPath']).split("\\")[len(str(shareResponseContent['binPath']).split("\\"))-2]
                                })
                                if compareResponse.status == 200:        
                                  # print(await response.text())        
                                  compareResponse =  json.loads(await compareResponse.text())  # 获取返回的内容
                                  shareRes = compareResponse['result']['shareRes']
                                  recheckRes = shareRes

                                  # 将复检结果保存到本地

                                  # 定义文件的名称
                                  localfilename = "temp.txt"

                                  # 打开文件，如果文件不存在会新建一个。使用模式'w'表示写入，并且会覆盖文件中原有的内容
                                  # with open(localfilename, 'w') as file:
                                  #     # 往文件中写入字符串
                                  #     file.write(str(serialNumber)+':'+str(shareRes))

                                  try:
                                      # 打开文件，如果文件不存在会新建一个。使用模式'w'表示写入，并且会覆盖文件中原有的内容
                                      with open(localfilename, 'w') as file:
                                          # 往文件中写入字符串
                                          file.write(str(serialNumber)+':'+str(shareRes))
                                          file.flush()  # 确保数据被写入操作系统的缓冲区
                                          os.fsync(file.fileno())  # 强制将文件的内容写入磁盘                  
                                      current_time = self.get_current_datetime()
                                      msg = f" {current_time}----- 数据成功写入文件：： {str(serialNumber)+':'+str(shareRes)}\r\n"
                                      self.log_message(msg, file_path)
                                      # print("数据成功写入文件")
                                  except IOError as e:
                                      # print(f"文件操作失败: {e}")
                                      current_time = self.get_current_datetime()
                                      msg = f" {current_time}----- 文件操作失败：： {e}\r\n"
                                      self.log_message(msg, file_path)
                                  except Exception as e:
                                      # print(f"发生了一个错误: {e}")
                                      current_time = self.get_current_datetime()
                                      msg = f" {current_time}----- 写入发生了一个错误：： {e}\r\n"
                                      self.log_message(msg, file_path)
                                  # 文件自动关闭
                                  current_time = self.get_current_datetime()
                                  msg = f" {current_time}----- 复检结果获取：： {shareRes}\r\n"
                                  self.log_message(msg, file_path)
                                  # 将结果存入数据库

                                  binCompareParams = {
                                      'orderNumber': orderNumber,
                                      'standard_string_original': str(shareResponseContent['standard_string']).upper(),
                                      'compare_string_original' : decrypted_str.upper(),
                                      'recheckEmployees': 'autoRecheck',
                                      'errMsg': '' if shareRes else '写码前后对比结果失败',
                                      'erpSerialNumber': self.erpResponse['data']['serialNo'],
                                      'erpManufacturer': self.erpResponse['data']['manufacturerName'],
                                      'erpModel': self.erpResponse['data']['modelName'],            
                                      'erpDate': '',
                                      'erpWavelength': self.erpResponse['data']['waveLength'],
                                      'erpRate': self.erpResponse['data']['speed'],
                                      'erpRateMin': '',
                                      'erpRateMax': '',
                                      'erpDistance': self.erpResponse['data']['transmissionDistance'],
                                      #box/共享 读取bin文件基本信息
                                      'binSerialNumber': serialNumber if serialNumber else self.erpResponse['data']['serialNo'],
                                      'binManufacturer': parsed_data['manufacturer'],
                                      'binModel': parsed_data['model'],
                                      'binDate': parsed_data['date'],
                                      'binWavelength': parsed_data['wavelength'],
                                      'binRate': parsed_data['rate'],               
                                      'binDistance': parsed_data['distance'],
                                      'diffKeyRes': '',
                                      'reMarks': '',
                                      'isArtificial': False,
                                      'isFinal': True,
                                      'compareCloumns': '',
                                      'standard_string':  compareResponse['result']['standard_string'],
                                      'compare_string': compareResponse['result']['compare_string'],
                                      'standard_string_parse': compareResponse['result']['standard_string_parse'],
                                      'compare_string_parse': compareResponse['result']['compare_string_parse'],
                                      'erpRes': '',
                                      'shareRes': '1' if compareResponse['result']['shareRes'] else '0',
                                      'checkRes': compareResponse['result']['shareRes'],
                                      'isAuto' : True
                                  }
                                  saveRecheckInfoApi = 'http://10.36.230.108:5002/pyapi/save_recheck_info_all'
                                  # 设置 Content-Type 为 application/json
                                  headers = {'Content-Type': 'application/json'}  
                                  # 使用 json 参数而不是 data 参数，aiohttp 会自动处理 JSON 的序列化和设置正确的 Content-Type 头
                                  recheckSaveResponse = await session.post(saveRecheckInfoApi, headers=headers, json=binCompareParams)
                                  if recheckSaveResponse.status == 200:
                                    current_time = self.get_current_datetime()
                                    recheckSaveResponse =  json.loads(await recheckSaveResponse.text())  # 获取返回的内容，
                                    msg = f" {current_time}----- 复检数据成功存入数据库----{recheckSaveResponse}\r\n"
                                    self.log_message(msg, file_path)
                                    self.log_message('-------------------------------------------------------------\r\n', file_path)
                                  else:
                                    current_time = self.get_current_datetime()
                                    msg = f" {current_time}--recheckSaveResponse---调用save_recheck_info_all--异常----结果：{recheckSaveResponse}\r\n"
                                    self.log_message(msg, file_path)
                        
                                else:
                                    current_time = self.get_current_datetime()
                                    msg = f" {current_time}--compareResponse---调用compare_hex_strings_api异常------结果：{compareResponse}\r\n"
                                    self.log_message(msg, file_path)
                      else:
                        current_time = self.get_current_datetime()
                        msg = f" {current_time}--shareResponse---调用getSharePublicDataBin--异常----结果：{recheckSaveResponse}\r\n"
                        self.log_message(msg, file_path)
                    wx.CallAfter(self.add_data_to_grid, parsed_data, '成功' if recheckRes else '失败')
                else:
                    print("Invalid SerialNumber or OriginBase64")
        except json.JSONDecodeError as e:
            current_time = self.get_current_datetime()
            msg = f" {current_time}--JSON解析错误--- f{e}\r\n"
            self.log_message(msg, file_path)
            self.log_message('-------------------------------------------------------------\r\n', file_path)
            logging.error(f'JSON解析错误: {e}', exc_info=True)
            
        except KeyError as e:
            current_time = self.get_current_datetime()
            msg = f" {current_time}--键错误: 缺少预期的键--- f{e}\r\n"
            self.log_message(msg, file_path)
            self.log_message('-------------------------------------------------------------\r\n', file_path)
            logging.error(f'键错误: 缺少预期的键 {e}', exc_info=True)
            
        except aiohttp.ClientError as e:
            current_time = self.get_current_datetime()
            msg = f" {current_time}---网络请求错误-- f{e}\r\n"
            self.log_message(msg, file_path)
            self.log_message('-------------------------------------------------------------\r\n', file_path)
            logging.error(f'网络请求错误: {e}', exc_info=True)
            
        except Exception as e:
            current_time = self.get_current_datetime()
            msg = f" {current_time}---处理消息时未知错误-- f{e}\r\n"
            self.log_message(msg, file_path)
            self.log_message('-------------------------------------------------------------\r\n', file_path)
            logging.error(f'处理消息时未知错误: {e}', exc_info=True)
    
    # 格式化清点时间，便于合成共享文件夹路径
    def format_datetime(self,original_datetime_str, index):
        # 将字符串转换为datetime对象
        datetime_obj = datetime.strptime(original_datetime_str, "%Y-%m-%d %H:%M:%S")
        
        # 提取年、月、日
        year = datetime_obj.year
        month = datetime_obj.month  # 月份去除前导零
        day_with_leading_zero = datetime_obj.strftime("%d")  # 日期保留前导零
        if index == 1:
          # 按照要求组装字符串，月份去除前导零，日期先去除后保留前导零
          formatted_datetime_str = f"{year}年\\{month}月\\{month}-{day_with_leading_zero}"
        else:
          formatted_datetime_str = f"{year}\\{month}月\\{datetime_obj.day}"
        return formatted_datetime_str

    def add_data_to_grid(self, data, res):
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        remark = ""
        result = res

        new_row_index = 0  # 新数据添加到第一行
        self.grid.InsertRows(pos=new_row_index, numRows=1)  # 在第一行插入新行
        self.grid.SetCellValue(new_row_index, 0, str(self.serial_counter))  # 设置序号
        self.grid.SetCellValue(new_row_index, 1, data.get('serial_number', ''))  # 设置sn
        self.grid.SetCellValue(new_row_index, 2, current_time)  # 设置复检时间
        self.grid.SetCellValue(new_row_index, 3, remark)  # 设置备注信息
        if result == "失败":
          self.grid.SetCellTextColour(new_row_index, 4, "red")
        self.grid.SetCellValue(new_row_index, 4, result)  # 设置复检结果

        self.serial_counter += 1
        self.grid.SetRowMinimalHeight(new_row_index, 40)

def parse_bin(serial_number, bin_data):
    private_key = 'FSBOX' + serial_number if len(serial_number) < 16 else serial_number
    private_key = private_key.ljust(32, ' ')  # 对齐到32字节
    iv = private_key[:16]  # 取密钥前16为作为偏移量
    private_key_bytes = private_key.encode('utf-8')
    iv_bytes = iv.encode('utf-8')

    origin_base64 = bin_data  # 待解密内容

    cipher = AES.new(private_key_bytes, AES.MODE_CBC, iv_bytes)
    decrypted_data = unpad(cipher.decrypt(binascii.a2b_base64(origin_base64)), AES.block_size)

    return decrypted_data.decode('utf-8')

async def main():
    app = wx.App()
    frame = MyFrame()
    frame.Show()
    await asyncio.gather(wxasync.WxAsyncApp().MainLoop(), frame.start_websocket())

try:
 asyncio.run(main())
except Exception as e:
  logging.error(f'未处理的异常: {e}', exc_info=True)
